/*    file: SdlPresenter.cpp
 *    desc:
 * 
 * created: 2014-04-24 11:16:09
 *  author: zhengchuanjiang
 * company: 
 */

#include "stdafx.h"
#include "SdlPresenter.h"

namespace sdl
{

Presenter::Presenter():
    m_texture(),
    m_renderer()
{

}

Presenter::~Presenter()
{
    reset();
}

void Presenter::reset()
{
    m_texture.reset();
    m_renderer = NULL;
}

bool Presenter::draw(SDL_Renderer* renderer, AVFrame* pFrame, int ratioX, int ratioY)
{
    if (!updateTexture(renderer, pFrame))
    {
        return false;
    }

    return drawCache(renderer, ratioX, ratioY);
}

bool Presenter::draw(SDL_Renderer* renderer, AVFrame* pFrame)
{
    if (!updateTexture(renderer, pFrame))
    {
        return false;
    }

    return drawCache(renderer, 0, 0);
}

bool Presenter::drawCache(SDL_Renderer* renderer, int ratioX, int ratioY)
{
    if (!m_texture)
    {
        return false;
    }

    int winWidth = 0;
    int winHeight = 0;
    SDL_GetRendererOutputSize(renderer, &winWidth, &winHeight);

    int w = 0;
    int h = 0;
    SDL_QueryTexture(m_texture.get(), NULL, NULL, &w, &h);

    SDL_Rect rcSrc = {0, 0, w, h};
    SDL_Rect rcDest = getScaleRect(
            w, h,
            winWidth, winHeight,
            ratioX, ratioY);

    rcDest.x = (rcDest.x / 2) * 2;
    rcDest.y = (rcDest.y / 2) * 2;
    rcDest.w = (rcDest.w / 2) * 2;
    rcDest.h = (rcDest.h / 2) * 2;

    SDL_RenderCopy(renderer, m_texture.get(), NULL, &rcDest);

    return true;
}

void Presenter::resetTexture(SDL_Renderer* renderer, int width, int height)
{
    if (m_texture)
    {
        int w = 0;
        int h = 0;
        SDL_QueryTexture(m_texture.get(), NULL, NULL, &w, &h);

        if ((w != width) || (h != height) || (m_renderer != renderer))
        {
            m_texture.reset();
        }
    }

    if (!m_texture)
    {
        SDL_Texture* pTexture = SDL_CreateTexture(renderer, 
            SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, width, height);
        m_texture.reset(pTexture, SDL_DestroyTexture);
        m_renderer = renderer;
    }
}

bool Presenter::updateTexture(SDL_Renderer* renderer, AVFrame* pFrame)
{
    resetTexture(renderer, pFrame->width, pFrame->height);
    if (!m_texture)
    {
        return false;
    }

    SDL_UpdateYUVTexture(m_texture.get(), NULL,
        pFrame->data[0], pFrame->linesize[0],
        pFrame->data[1], pFrame->linesize[1], 
        pFrame->data[2], pFrame->linesize[2]);

    return true;
}

SDL_Rect Presenter::getScaleRect(int srcWidth, int srcHeight, 
                                 int destWidth, int destHeight,
                                 int ratioX, int ratioY)
{
    SDL_Rect rc = {0, 0, destWidth, destHeight};
    if ((ratioX == 0) && (ratioY == 0))
    {
        // raw ratio
        rc = getScaleRect(rc, srcWidth, srcHeight);
    }
    else if ((ratioX < 0) || (ratioY < 0))
    {
        // fit window
    }
    else
    {
        rc = getScaleRect(rc, ratioX, ratioY);
    }
    return rc;
}

SDL_Rect Presenter::getScaleRect(const SDL_Rect& rc, int ratioX, int ratioY)
{
    SDL_Rect rcOut = rc;
    int diff = rc.w * ratioY - rc.h * ratioX;
    if (diff > 0)
    {
        // 保持垂直
        rcOut.y = rc.y;
        rcOut.h = rc.h;

        rcOut.w = rc.h * ratioX / ratioY;
        rcOut.x = rc.x + (rc.w - rcOut.w)/2;
    }
    else if (diff < 0)
    {
        // 保持水平
        rcOut.x = rc.x;
        rcOut.w = rc.w;
        rcOut.h = rc.w * ratioY / ratioX;
        rcOut.y = rc.y + (rc.h - rcOut.h)/2;
    }
    return rcOut;   
}


}


